/*
 * Copyright (c) 2020. www.sonsure.com Inc. All rights reserved.
 * You may obtain more information at
 *
 *   http://www.sonsure.com
 *
 * Designed By Selfly Lee (selfly@live.com)
 */

package com.sonsure.dustman.jdbc.command;


import com.sonsure.dustman.common.bean.BeanKit;
import com.sonsure.dustman.common.model.Page;
import com.sonsure.dustman.common.model.Pagination;
import com.sonsure.dustman.jdbc.command.build.*;
import com.sonsure.dustman.jdbc.command.simple.ResultHandler;
import com.sonsure.dustman.jdbc.config.JdbcContext;
import com.sonsure.dustman.jdbc.exception.SonsureJdbcException;
import com.sonsure.dustman.jdbc.mapping.MappingHandler;
import lombok.Getter;
import lombok.Setter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * The type Abstract command executor.
 *
 * @author liyd
 * @since 17 /4/19
 */
@Getter
@Setter
public abstract class AbstractCommandExecutor<E extends CommandExecutor<E>> implements CommandExecutor<E> {

    private JdbcContext jdbcContext;
    private ExecutableCmdBuilder executableCmdBuilder;

    public AbstractCommandExecutor(JdbcContext jdbcContext) {
        this.jdbcContext = jdbcContext;
        this.executableCmdBuilder = new ExecutableCmdBuilder();
        this.executableCmdBuilder.jdbcContext(jdbcContext);
    }

    @Override
    public E forceNative() {
        this.getExecutableCmdBuilder().forceNative();
        return this.getSelf();
    }

    @Override
    public E namedParameter() {
        this.getExecutableCmdBuilder().namedParameter();
        return this.getSelf();
    }

    protected <T> Page<T> doPageResult(ExecutableCmd executableCmd, PageQueryHandler<T> pageQueryHandler) {
        Pagination pagination = executableCmd.getPagination();
        if (pagination == null) {
            throw new SonsureJdbcException("查询分页列表请设置分页信息");
        }
        String dialect = this.getDatabaseProduct();
        long count = Long.MAX_VALUE;
        if (!executableCmd.isDisableCountQuery()) {
            String countCommand = getJdbcContext().getPageHandler().getCountCommand(executableCmd.getCommand(), dialect);
            ExecutableCmd countExecutableCmd = BeanKit.copyProperties(new ExecutableCmd(), executableCmd);
            countExecutableCmd.setCommand(countCommand);
            countExecutableCmd.setExecutionType(ExecutionType.FIND_ONE_FOR_SCALAR);
            countExecutableCmd.setResultType(Long.class);
            Object result = getJdbcContext().getPersistExecutor().execute(countExecutableCmd);
            count = (Long) result;
        }
        pagination.setTotalItems((int) count);
        String pageCommand = getJdbcContext().getPageHandler().getPageCommand(executableCmd.getCommand(), pagination, dialect);
        ExecutableCmd pageExecutableCmd = BeanKit.copyProperties(new ExecutableCmd(), executableCmd);
        pageExecutableCmd.setCommand(pageCommand);
        List<T> list = pageQueryHandler.queryList(pageExecutableCmd);

        return new Page<>(list, pagination);
    }

    protected String getDatabaseProduct() {
        ExecutableCmd gdpCmd = new ExecutableCmd();
        gdpCmd.setExecutionType(ExecutionType.GET_DATABASE_PRODUCT);
        gdpCmd.setJdbcContext(this.getJdbcContext());
        return (String) getJdbcContext().getPersistExecutor().execute(gdpCmd);
    }

    protected <T> T handleResult(Object result, ResultHandler<T> resultHandler) {
        if (result == null) {
            return null;
        }
        return resultHandler.handle(result);
    }

    protected <T> List<T> handleResult(List<?> result, ResultHandler<T> resultHandler) {
        if (result == null) {
            return Collections.emptyList();
        }
        List<T> resultList = new ArrayList<>();
        for (Object obj : result) {
            T e = this.handleResult(obj, resultHandler);
            resultList.add(e);
        }
        return resultList;
    }

    protected <T> Page<T> handleResult(Page<?> page, ResultHandler<T> resultHandler) {
        Page<T> newPage = new Page<>(page.getPagination());
        if (page.getList() == null || page.getList().isEmpty()) {
            return newPage;
        }
        List<T> resultList = new ArrayList<>();
        for (Object obj : page.getList()) {
            T e = this.handleResult(obj, resultHandler);
            resultList.add(e);
        }
        newPage.setList(resultList);
        return newPage;
    }

    protected <T> String lambda2Field(GetterFunction<T> getter) {
        LambdaGetter lambdaGetter = LambdaHelper.getLambdaGetter(getter);
        String tableAlias = this.getExecutableCmdBuilder().resolveTableAlias(lambdaGetter.getSimpleClassName());
        return CommandBuildHelper.getTableAliasFieldName(tableAlias, lambdaGetter.getFieldName());
    }

    @SuppressWarnings("unchecked")
    protected E getSelf() {
        return (E) this;
    }

    protected void registerClassToMappingHandler(Class<?> cls) {
        MappingHandler mappingHandler = this.getJdbcContext().getMappingHandler();
        mappingHandler.registerClassMapping(cls.getSimpleName(), cls);
    }

    protected interface PageQueryHandler<T> {
        /**
         * Query list.
         *
         * @param executableCmd the executable cmd
         * @return the list
         */
        List<T> queryList(ExecutableCmd executableCmd);
    }
}
